/**
 * 
 */
package dataflowscheme.engine.core.launching;

import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.launching.AbstractJavaLaunchConfigurationDelegate;
import org.eclipse.jdt.launching.ExecutionArguments;
import org.eclipse.jdt.launching.IVMRunner;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.VMRunnerConfiguration;

/**
 * Delegate to launch data-flow engine from eclipse launcher
 * 
 * @author <a href="mailto:misiak7@gmail.com">Michal Antolik</a>
 *
 */
public class DFSLaunchDelegate extends AbstractJavaLaunchConfigurationDelegate{
	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.model.ILaunchConfigurationDelegate#launch(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String, org.eclipse.debug.core.ILaunch, org.eclipse.core.runtime.IProgressMonitor)
	 */
	@SuppressWarnings("unchecked")
	public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException {
		
		if (monitor == null) {
			monitor = new NullProgressMonitor();
		}
		
		monitor.beginTask(MessageFormat.format("{0}...", new Object[]{configuration.getName()}), 3); //$NON-NLS-1$
		// check for cancellation
		if (monitor.isCanceled()) {
			return;
		}
		try {
			monitor.subTask("Verifying launch attributes..."); 
							
			String mainTypeName = "dataflowscheme.engine.core.Runner";
			IVMRunner runner = getVMRunner(configuration, mode);
	
			//acquire working directory
			File workingDir = verifyWorkingDirectory(configuration);
			String workingDirPath = null;
			if (workingDir != null) {
				workingDirPath = workingDir.getAbsolutePath();
			}
			
			
			
			// Environment variables
			String[] envp= getEnvironment(configuration);
			
			// Program & VM arguments
			if(!configuration.hasAttribute(IDFConstants.ATTR_DF_SCHEME)){
				abort("No data-flow scheme has been selected.", null);
			}
			
			//workspace path
			String workspaceAP = Platform.getLocation().toString() + "/";
			
			//a data-flow scheme path
			String dataflowScheme = configuration.getAttribute(IDFConstants.ATTR_DF_SCHEME, (String)null);
			
			//build program arguments
			String pgmArgs = workspaceAP + dataflowScheme.substring(1);
			
			//log4j properties file
			if(configuration.hasAttribute(IDFConstants.ATTR_DF_SCHEME_LOG4JFILE)){
				String log4jFile = configuration.getAttribute(IDFConstants.ATTR_DF_SCHEME_LOG4JFILE, (String)null);
				if(!log4jFile.trim().equals("")){
					log4jFile = workspaceAP + log4jFile.substring(1);
					pgmArgs += " log4jFile=\"" + log4jFile + "\"";
				}
			}
			
			//enabling statistics
			if(configuration.hasAttribute(IDFConstants.ATTR_DF_SCHEME_STATS)){
				pgmArgs += " stats=" + configuration.getAttribute(IDFConstants.ATTR_DF_SCHEME_STATS, (String)null);
			}
			
			//setting log4j log level
			if(configuration.hasAttribute(IDFConstants.ATTR_DF_SCHEME_LOGLEVEL)){
				pgmArgs += " logLevel=" + configuration.getAttribute(IDFConstants.ATTR_DF_SCHEME_LOGLEVEL, (String)null);
			}
			
			String vmArgs = getVMArguments(configuration);
			ExecutionArguments execArgs = new ExecutionArguments(vmArgs, pgmArgs);
			
			// VM-specific attributes
			Map vmAttributesMap = getVMSpecificAttributesMap(configuration);
			
			// build class-path
			String[] classpath = getClasspath(configuration);
			
			List<String> classPathList = new ArrayList<String>();
			Collections.addAll(classPathList, classpath);

			//add user files to class-path
			String javaProjectName = dataflowScheme.substring(1, dataflowScheme.indexOf("/", 1));
			IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(javaProjectName);
			IJavaProject jproject = JavaCore.create(project);
			String[] projectClasspath = JavaRuntime.computeDefaultRuntimeClassPath(jproject);
			Collections.addAll(classPathList, projectClasspath);
			
			//add engine libraries to class-path
			String librariesDir = getLibrariesDirPath();
			if(librariesDir != null){
				classPathList.add(librariesDir + "dataflow-library.jar");
				classPathList.add(librariesDir + "org.eclipse.emf.common_2.4.0.jar");
				classPathList.add(librariesDir + "org.eclipse.emf.ecore_2.4.2.jar");
				classPathList.add(librariesDir + "org.eclipse.emf.ecore.xmi_2.4.1.jar");
				classPathList.add(librariesDir + "log4j-1.2.15.jar");
			}
			classpath = classPathList.toArray(classpath);
			
			// Create VM config
			VMRunnerConfiguration runConfig = new VMRunnerConfiguration(mainTypeName, classpath);
			runConfig.setProgramArguments(execArgs.getProgramArgumentsArray());
			runConfig.setEnvironment(envp);
			runConfig.setVMArguments(execArgs.getVMArgumentsArray());
			runConfig.setWorkingDirectory(workingDirPath);
			runConfig.setVMSpecificAttributesMap(vmAttributesMap);
	
			// Bootpath
			runConfig.setBootClassPath(getBootpath(configuration));
			
			// check for cancellation
			if (monitor.isCanceled()) {
				return;
			}		
			
			// stop in main
			prepareStopInMain(configuration);
			
			// done the verification phase
			monitor.worked(1);
			
			monitor.subTask("Creating source locator..."); 
			// set the default source locator if required
			setDefaultSourceLocator(launch, configuration);
			monitor.worked(1);		
			
			// Launch the configuration - 1 unit of work
			runner.run(runConfig, launch, monitor);
			
			// check for cancellation
			if (monitor.isCanceled()) {
				return;
			}	
		}
		finally {
			monitor.done();
		}
	}
	
	/**
	 * Throws an exception with a new status containing the given
	 * message and optional exception.
	 * 
	 * @param message error message
	 * @param e underlying exception
	 * @throws CoreException
	 */
	private void abort(String message, Throwable e) throws CoreException {
		// TODO: the plug-in code should be the example plug-in, not Perl debug model id
		throw new CoreException(new Status(IStatus.ERROR, IDFConstants.ID_DFS_DEBUG_MODEL, 0, message, e));
	}
	
	private String getLibrariesDirPath(){
		String returnValue = null;
		try {
			returnValue = FileLocator.toFileURL(Platform.getBundle("dataflowscheme.engine.core").
					getEntry("libraries")).getFile();
			returnValue = returnValue.substring(1);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return returnValue;
	}

}
